<?

/* GameModel model class.
 * This class contains data model implementation code.
 */ 
/* @author Udo Schroeter <udo.schroeter@gmail.com>
 * @license dual license: LGPL / commercial use license
 */ 

class GameModel extends AGModel
{
  function __construct($modelName) 
  {
    parent::__construct($modelName);
    $this->fixedMapAPCost = 0;
  }
  
  function getLocations(&$stats, $type)
  {
    $result = array();
    foreach(explode(',', $stats[$type]) as $i)
    {
      $coords = explode('-', $i);
      if (!isset($coords[1])) $coords[1] = $coords[0];
      $c1 = explode(':', $coords[0]); $c2 = explode(':', $coords[1]);
      $result[] = array('x1' => $c1[0], 'y1' => $c1[1], 'x2' => $c2[0], 'y2' => $c2[1]);
    }
    return($result);
  }
  
  function isInLocation($locations, $xpos = null, $ypos = null)
  {
    $matches = false;
    if ($xpos == null) $xpos = $this->mstats['xpos']; 
    if ($ypos == null) $ypos = $this->mstats['ypos']; 
    foreach($locations as $checkPass)
    {
      if ($checkPass['x1'] <= $xpos 
        && $checkPass['x2'] >= $xpos
        && $checkPass['y1'] <= $ypos
        && $checkPass['y2'] >= $ypos)
        $matches = true;
    }  
    return($matches);
  }
  
  function canGoToRelative($relX, $relY)
  {
    return(!$this->isInLocation(
      $this->getLocations($this->parent->zoneStats, 'impassable'),
      $this->mstats['xpos']+$relX, $this->mstats['ypos']+$relY));
  }
  
  function getDistance($x1, $y1, $x2, $y2)
  {
    $xd = $x1-$x2;
    $yd = $y1-$y2;
    return(floor(sqrt($xd*$xd+$yd*$yd)));
  }
  
  function moveCharacterOnMap($direction)
  {
    $zoneStats = &$this->parent->zoneStats;
    $mapDS = DB_GetDatasetMatch('mapstates', array(
      'ms_character' => $this->parent->character->charDS['c_key'],
      'ms_zone' => $this->parent->currentZone, 
      ));
    $this->mstats = TextToStringlist($mapDS['ms_state']);
    $this->mstats['xpos'] = getDefault($this->mstats['xpos'], $this->parent->zoneStats['xpos']);
    $this->mstats['ypos'] = getDefault($this->mstats['ypos'], $this->parent->zoneStats['ypos']);
    $px = $this->mstats['xpos']; $py = $this->mstats['ypos'];
    $walked = false;
    $dirName = array(
      'n' => 'north',
      's' => 'south',
      'e' => 'east',
      'w' => 'west',
      );
    switch($direction)
    {
      case('w'): {
        if ($this->mstats['xpos'] > 0)
        {
          $this->mstats['xpos']--;
          $walked = true;
        }
        break;
      }
      case('e'): {
        if ($this->mstats['xpos'] < 9)
        {
          $this->mstats['xpos']++;
          $walked = true;
        }
        break;
      }
      case('n'): {
        if ($this->mstats['ypos'] > 0)
        {
          $this->mstats['ypos']--;        
          $walked = true;
        }
        break;
      }
      case('s'): {
        if ($this->mstats['ypos'] < 7)
        {
          $this->mstats['ypos']++;
          $walked = true;
        }
        break;
      }
    }
    if ($walked && $this->character->getCharacterStat('AP') >= $this->fixedMapAPCost)
    {
      $canPass = !$this->isInLocation($this->getLocations($zoneStats, 'impassable'));
      if ($canPass)
      {
        $this->mstats['explored.'.$px.'.'.$py] = true;
        unset($this->mstats['currentlocation']);
        $this->mstats['loclist'] = array();
        foreach(explode(',', $zoneStats['locations']) as $locId)
        {
          if ($this->isInLocation($this->getLocations($zoneStats, $locId.'.map')))
          {
            $this->mstats['loclist'][] = $locId;
            $this->mstats['currentlocation'] = $locId;
          }
        }       
        $this->mstats['loclist'] = implode(',', $this->mstats['loclist']);
        $mapDS['ms_state'] = StringlistToText($this->mstats);
        DB_UpdateDataset('mapstates', $mapDS);
        $this->character->stats['AP.current'] =
          $this->character->getCharacterStat('AP')-$this->fixedMapAPCost;
        $this->character->saveCharacter();
      }
      else
      {
        $this->mapMessage = 'You cannot pass this terrain towards the '.$dirName[$direction].'!';
      }
    }
  }
  
  function makeDropList($lootList)
  {
    ob_start();
    $buffList = array();
    foreach($lootList as $lootItem)
    {
      if ($lootItem['type'] == 'buff')
      {
        if ($this->character->stats[$lootItem['stat']] < $lootItem['value'])
          $this->character->stats[$lootItem['stat']] += $lootItem['value'];
        if (substr($lootItem['stat'], -6) == '.magic')
          $this->character->heal($lootItem['value'], substr($lootItem['stat'], 0, -6));
        $buffList[] = $lootItem['value'].' '.AGController::l10n($lootItem['stat'], 'cq');
      }
      else if ($lootItem['type'] == 'party')
      {
        $pm = DB_GetDataset('characters', $lootItem['c_key']);
        $partyDS = DB_GetDatasetMatch('parties', array(
          'p_party' => $this->character->charDS['c_ref_user'],
          'p_member' => $lootItem['c_key'],
          'p_type' => 'mob',
          ));
        DB_UpdateDataset('parties', $partyDS);
        ?><li>You have gained a new party member: <?= $pm['c_name'] ?></li><?
      }
      else
      {
        $itemDS = DB_GetDataset('items', $lootItem['i_key']);
        if (sizeof($itemDS) == 0) $itemDS = DB_GetDataset('items', $lootItem['i_key'], 'i_shorthand');
        if (sizeof($itemDS) != 0)
        {
          print('<li>'.$lootItem['#'].'x '.$itemDS['i_name']);
          $this->character->addInventory($itemDS, $lootItem['#']);
          if ($itemDS['i_type'] == 'wearable')
            print(', <a class="linkbutton" href="'.actionUrl('index', 'character', 'on='.$itemDS['i_key']).'">put on / equip</a> now');
          print('</li>');
        }
      }
    }  
    if (sizeof($buffList) > 0)
    {
      $this->character->recalculateStats();
      $this->character->saveCharacter();
      ?><li>You have gained <?= implode(', ', $buffList) ?></li><?
    }
    return(trim(ob_get_clean()));
  }

  function loadZone($zone_id)
  {
    $this->zoneDS = DB_GetDataset('zones', $zone_id);
    $this->zoneStats = TextToStringList($this->zoneDS['z_stats']);
    $histDS = DB_GetDatasetMatch('history', array(
      'h_character' => $this->character->charDS['c_key'],
      'h_type' => 'zone',
      'h_zone' => $zone_id,
      ));
    if (!isset($histDS['h_key']))
    {
      // first time in this zone!
      include_once('lib/combat.php');
      DB_UpdateDataset('history', $histDS);
      $lootMessage = $this->makeDropList(dropItems($this->zoneStats));
      if ($lootMessage != '')
        $this->zoneMessage = 'You now have:<ul>'.$lootMessage.'</ul>';
      AdminNotify($this->character->charDS['c_name'].'('.$this->character->level.') now in zone: '.$this->zoneDS['z_name']);
      // post a feed story!
      if (trim($this->zoneDS['z_poststory']) != '')
      {
        $tokens = array(
          'location' => $this->zoneDS['z_poststory']);
        $target_ids = array(); $body_general = ''; 
        global $ctrObj;
        if (!$this->user)
          $this->user = &$ctrObj->user;
        if ($this->user)
          $this->user->fb->api_client->feed_publishUserAction(
            '63392752028', $tokens , implode(',', $target_ids), $body_general);    
      }
    }
    if (isset($this->zoneStats['rezone']))
    {
      $this->character->charDS['c_location'] = $this->zoneStats['rezone'];
      $this->moveNextButton = getDefault($this->zoneStats['rezone.button'], 'OK');
      $this->character->saveCharacter();
    }
  }
  
  function loadAction($actionId)
  {
    $this->act = DB_GetDataset('actions', $actionId);
    $this->act['properties'] = TextToStringList($this->act['a_stats']);
  }
  
  // static
  function checkValue($val1, $operator, $val2)
  {
    $n1 = trim($val1)+0;
    $n2 = trim($val2)+0;
    switch($operator)
    {
      case('equal'): {
        return($n1 == $n2);
        break;
      }
      case('at_least'): {
        return($n1 >= $n2);
        break;
      }
      case('higher'): {
        return($n1 > $n2);
        break;
      }
      case('lower'): {
        return($n1 < $n2);
        break;
      }
    }
    return(false);
  }
  
  // static
  function meetsPrerequisite($line, &$char, $silent = true)
  {
    $result = true;
    $seg = explode(' ', trim($line));
    $stype = $seg[0];
    $fieldname = $seg[1];
    $operator = $seg[2];
    $compareto = $seg[3];
    $failtext = $seg[4];
    $txt = '';
    $linefailed = false;
    switch($stype)
    {
      case('history'): {
        // any field of a history dataset
        $matchfields = array('h_character' => $char->charDS['c_key']);
        foreach(explode(',', $fieldname) as $fval)
        {
          $k = CutSegment('=', $fval);
          $matchfields[trim($k)] = trim($fval);
        }
        $histDS = DB_GetDatasetMatch('history', $matchfields, false);
        if (!GameModel::checkValue($histDS[$operator], $compareto, $failtext))
        {
          for($a = 0; $a < 5; $a++) unset($seg[$a]);
          $txt = implode(' ', $seg);
          $result = false;
          $linefailed = true;
        }
        break;
      }
      case('char'): {
        // any field of the character dataset
        $txt = 'Your '.AGController::l10n($fieldname, 'cq').' must be '.
          AGController::l10n($operator, 'cq', 'en').' '.$compareto;
        if (!GameModel::checkValue($char->charDS[$fieldname], $operator, $compareto))
        {
          $result = false;
          $linefailed = true;
        }
        break;
      }
      case('stats'): {
        $txt = AGController::l10n($fieldname, 'cq').' must be '.
          AGController::l10n($operator, 'cq').' '.$compareto;
        if (!GameModel::checkValue($char->getCharacterStat($fieldname), $operator, $compareto))
        {
          $result = false;
          $linefailed = true;
        }
        break;
      }
      case('inv'): {
        $itemDS = DB_GetDataset('items', $fieldname);
        $invDS = DB_GetDatasetWQuery('SELECT * FROM '.getTableName('inventory').'
          WHERE iv_character="'.$char->charDS['c_key'].'" AND iv_item="'.$fieldname.'"');
        if ($operator == 'at_least' && $compareto == 1)
          $txt = 'You need a '.$itemDS['i_name'].'';
        else
          $txt = 'You need '.AGController::l10n($operator, 'cq').' '.$compareto.' '.
            makePlural($itemDS['i_name'], $compareto);
        if ($invDS['iv_quantity'] != $compareto)
          $txt .= ' (you have '.($invDS['iv_quantity']+0).')';
        if (!GameModel::checkValue($invDS['iv_quantity'], $operator, $compareto))
        {
          $result = false;
          $linefailed = true;
        }
        break;
      }
    }
    if (!$silent)
    {
      if ($linefailed)
        $txt = '<b style="color: red">'.$txt.'</b>';
      else
        $txt = '<span style="color: gray">'.$txt.'</span>';
      ?><div><?= $txt ?></div><?
    }
    return($result);
  }
  
  function printPrerequisites($preText, &$char, $silent = false)
  {
    $result = true;
    // fixme: unify with meetsPrerequisite()
    foreach(explode(chr(13), $preText) as $line)
    {
      $seg = explode(' ', trim($line));
      $stype = $seg[0];
      $fieldname = $seg[1];
      $operator = $seg[2];
      $compareto = $seg[3];
      $failtext = $seg[4];
      $txt = '';
      $linefailed = false;
      switch($stype)
      {
        case('history'): {
          // any field of a history dataset
          $matchfields = array('h_character' => $char->charDS['c_key']);
          foreach(explode(',', $fieldname) as $fval)
          {
            $k = CutSegment('=', $fval);
            $matchfields[trim($k)] = trim($fval);
          }
          $histDS = DB_GetDatasetMatch('history', $matchfields, false);
          if (!$this->checkValue($histDS[$operator], $compareto, $failtext))
          {
            for($a = 0; $a < 5; $a++) unset($seg[$a]);
            $txt = implode(' ', $seg);
            $result = false;
            $linefailed = true;
          }
          break;
        }
        case('char'): {
          // any field of the character dataset
          $txt = 'Your '.AGController::l10n($fieldname, 'cq').' must be '.
            AGController::l10n($operator, 'cq', 'en').' '.$compareto;
          if (!$this->checkValue($char->charDS[$fieldname], $operator, $compareto))
          {
            $result = false;
            $linefailed = true;
          }
          break;
        }
        case('stats'): {
          $txt = AGController::l10n($fieldname, 'cq').' must be '.
            AGController::l10n($operator, 'cq').' '.$compareto;
          if (!$this->checkValue($char->getCharacterStat($fieldname), $operator, $compareto))
          {
            $result = false;
            $linefailed = true;
          }
          break;
        }
        case('inv'): {
          $itemDS = DB_GetDataset('items', $fieldname);
          $invDS = DB_GetDatasetWQuery('SELECT * FROM '.getTableName('inventory').'
            WHERE iv_character="'.$char->charDS['c_key'].'" AND iv_item="'.$fieldname.'"');
          if ($operator == 'at_least' && $compareto == 1)
            $txt = 'You need a '.$itemDS['i_name'].'';
          else
            $txt = 'You need '.AGController::l10n($operator, 'cq').' '.$compareto.' '.
              makePlural($itemDS['i_name'], $compareto);
          if ($invDS['iv_quantity'] != $compareto)
            $txt .= ' (you have '.($invDS['iv_quantity']+0).')';
          if (!$this->checkValue($invDS['iv_quantity'], $operator, $compareto))
          {
            $result = false;
            $linefailed = true;
          }
          break;
        }
      }
      if (!$silent)
      {
        if ($linefailed)
          $txt = '<b style="color: red">'.$txt.'</b>';
        else
          $txt = '<span style="color: gray">'.$txt.'</span>';
        ?><div><?= $txt ?></div><?
      }
    }
    return($result);
  }
  
  function getActionListForZone($filter_action = null, $zone_id = null)
  {
    $result = array();
      
    if ($zone_id == null) $zone_id = $this->zoneDS['z_key'];
    if ($filter_action != null) $filter = ' AND a_key="'.$filter_action.'"'; else $filter = '';
    $zoneActions = DB_GetList('SELECT * FROM '.getTableName('actions').' 
      LEFT JOIN '.getTableName('history').' ON (h_character="'.$this->character->charDS['c_key'].'" AND h_action=a_key)
      WHERE a_zone="'.$zone_id.'" '.$filter.' 
      ORDER BY a_order,a_key');

    foreach($zoneActions as $action)
    {
      $result_item = $action;
      $result_item['properties'] = TextToStringList($action['a_stats']);
      if (getDefault($result_item['properties']['subtype'], $action['a_type']) == 'C')
      {
        $loadMobs = @explode(',', $result_item['properties']['mob']);
        if (is_array($loadMobs) > 0) foreach ($loadMobs as $mobParams)
        {
          $mpa = explode(':', $mobParams);
          $mobKey = $mpa[0];
          $mobCount = getDefault($mpa[1], 1);
          $mobDS = DB_GetDataset('characters', $mobKey+0);
          $mobDS['properties'] = TextToStringList($mobDS['c_stats']);
          for($mc = 0; $mc < $mobCount; $mc++) $result_item['mobs'][] = $mobDS;
          $result_item['mob_intros'][] = $mobDS['c_intro'];
          $result_item['mob_texts'][] = $mobDS['c_text'];
          $result_item['a_icon'] = getDefault($result_item['a_icon'], $mobDS['c_icon']);
          $this->combineStats($result_item['properties'], $mobDS['properties']);
        }
      }
      $result_item['a_intro'] = getDefault(
        $result_item['a_intro'], @implode(chr(13), $result_item['mob_intros']));
      $result_item['a_text'] = getDefault(
        $result_item['a_text'], @implode(chr(13), $result_item['mob_texts']));
      
      $result[] = $result_item;
    }
      
    return($result);
  }
  
  // this explains the drops
  function getDrops($actionDS)
  {
    $result = array();
    foreach($actionDS['properties'] as $p => $v) if (substr($p, 0, 5) == 'drop.')
    {
      CutSegment('.', $p);
      $type = CutSegment('.', $p);
      $params = explode(',', $v);
      switch($type)
      {
        case('gold'): {
          $item = $params[1].'-'.$params[2].' gold pieces';
          break;
        }
        default: {
          $itemDS = DB_GetDataset('items', $type);
          if (sizeof($itemDS) == 0) $itemDS = DB_GetDataset('items', $type, 'i_shorthand');
          if (sizeof($itemDS) == 0)
            $item = 'unknown item ID "'.$type.'"';
          else
          {
            $params[1] = getDefault($params[1], 1);
            $item = $params[1];
            if (isset($params[2])) $item .= '-'.$params[2];
            $item .= ' '.makePlural($itemDS['i_name'], getDefault($params[2], $params[1])+0);
          }
          break;
        }
      }
      if ($params[0] != '1' && $params[0] != '100%')
        $item .= ' ('.$params[0].' chance)';
      $result[] = $item;
    }
    if (sizeof($result) == 0) $result[] = 'nothing at all!';
    return($result);
  }
  
  function combineStats(&$stat1, $stat2)
  {
    if (is_array($stat2))
      foreach($stat2 as $k => $v)
        $stat1[$k] = getDefault($stat1[$k], $v);
  }
}


?>
